#include "ota_version.h"
#include "ota_http.h"
#include "cJSON/cJSON.h"
#include "log/log.h"
#include <unistd.h>
#include <openssl/sha.h>
#include <string.h>
#include <sys/reboot.h>
#include <stdlib.h>

/**
 * 获取文件的SHA1哈希值（40位16进制字符串）
 * 相同文件返回相同的哈希值，可用于判断文件是否相同
 * linux命令生成：sha1sum 文件名
 * 此函数可以利用AI工具帮我们生成
 */
static char *get_file_sha(char *filepath)
{
    FILE *file = fopen(filepath, "rb");
    if (!file)
    {
        perror("Failed to open file");
        return NULL;
    }

    unsigned char hash[SHA_DIGEST_LENGTH];
    SHA_CTX sha1;
    SHA1_Init(&sha1);

    const int bufSize = 32768;
    unsigned char *buffer = (unsigned char *)malloc(bufSize);
    if (!buffer)
    {
        perror("Failed to allocate memory");
        fclose(file);
        return NULL;
    }

    int bytesRead;
    while ((bytesRead = fread(buffer, 1, bufSize, file)) > 0)
    {
        SHA1_Update(&sha1, buffer, bytesRead);
    }

    SHA1_Final(hash, &sha1);
    fclose(file);
    free(buffer);

    char *outputBuffer = (char *)malloc(SHA_DIGEST_LENGTH * 2 + 1);
    if (!outputBuffer)
    {
        perror("Failed to allocate memory");
        return NULL;
    }

    for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }

    return outputBuffer;
}

/**
 * 版本检查更新
 1. 请求获取最新版本信息  json
 2. 解析json, 得到各个级别的版本号
 3. 比较版本号，如果当前版本不小于最新版本，则不需要更新
 4. 请求下载远程的固件，如果请求失败，则退出
 5. 下载成功后，计算本地文件sha值，得到获取的sha值
 6. 比较本地文件sha值和获取的sha值，如果相同，则更新成功(重启)，否则更新失败
 */
int ota_version_checkUpdate()
{
    // 请求获取最新版本信息  json
    char *json = ota_http_getJson(OTA_URL_FILEINFO);
    // 解析json, 得到各个级别的版本号
    cJSON *root_obj = cJSON_Parse(json);
    int major = cJSON_GetObjectItem(root_obj, "major")->valueint;
    int minor = cJSON_GetObjectItem(root_obj, "minor")->valueint;
    int patch = cJSON_GetObjectItem(root_obj, "patch")->valueint;
    log_debug("获取到最新版本号: %d.%d.%d", major, minor, patch);
    // 比较版本号，如果当前版本不小于最新版本，则不需要更新
    if (VERSION_MAJOR > major || (VERSION_MAJOR == major && VERSION_MINOR > minor || (VERSION_MAJOR == major && VERSION_MINOR == minor && VERSION_PATCH >= patch)))
    {
        log_debug("当前是最新版本，不需要更新");
        return 0;
    }
    // 请求下载远程的固件，如果请求失败，则退出
    // 删除旧的文件
    unlink(OTA_LOCAL_FILE_PATH);
    int result = ota_http_download(OTA_URL_DOWNLOAD, OTA_LOCAL_FILE_PATH);
    if (result == -1)
    {
        log_error("下载失败");
        return -1;
    }
    // 下载成功后，计算本地文件sha值，得到获取的sha值
    char *local_sha = get_file_sha(OTA_LOCAL_FILE_PATH);
    char *remote_sha = cJSON_GetObjectItem(root_obj, "sha1")->valuestring;
    log_debug("本地sha值: %s, 远程sha值: %s", local_sha, remote_sha);

    // 校验固件（比较两个sha值是否相同）， 如果不同，校验失败，退出
    if (strcmp(local_sha, remote_sha) != 0)
    {
        log_error("校验失败");
        // 释放资源
        free(local_sha);
        free(json);
        cJSON_Delete(root_obj);
        return -1;
    }

    log_debug("校验成功，准备重新系统安装运行新固件");
    // 如果校验成功，重新系统，启动新下载的固件程序
    reboot(RB_AUTOBOOT); //  当前测试不能重启 =》 当前不是root用户，没权限
}

/**
 * 每隔一天检查一次更新
 */
int ota_version_checkUpdateDaily()
{
    while (1)
    {
        ota_version_checkUpdate();
        log_debug("等待一天后再次检查更新");
        sleep(24 * 60 * 60); // 24小时
    }
    return 0;
}

/**
 * 打印当前版本号
 */
void ota_version_printVersion()
{
    log_debug("当前版本号：%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
}
